#include <poll.h>
#include <stdio.h>
#include <unistd.h>
#include "dbm/dbus-monitor.h"

typedef struct pollfd PollFD;
static PollFD pfds[DBM_MAX_WATCHES+1];
static int pfdc = 0; // fd count
static dbus_bool_t add_watch(DBusWatch *w, void *data);
static void remove_watch(DBusWatch *w, void *data);
static void dispatch(DBusMonitor *m);
static dbus_bool_t procCtlFD(DBusMonitor *m, PollFD *pfd);

int main(int argc, char *argv[]) {
    char addr[DBM_MAX_ADDR];
    FILE *f = popen("fcitx-remote -a", "re");
    int n = fread(addr, 1, DBM_MAX_ADDR, f);
    pclose(f);
    addr[n-1] = '\0';
    printf("%s\n", addr);
    fprintf(stderr, "输入中文方便观察效果\n");
    DBusMonitor *m = dbmCreateWithAddr(addr, add_watch, remove_watch, dispatch);
    dbmInitConnection(m);
    dbmAddRule(m, "interface=org.fcitx.Fcitx.InputContext");
    dbmAddHandler(m, dbmDefaultHandler, m, NULL);
    dbmStart(m);
    sleep(16);
    dbmStop(m);
    dbmDestroy(m);
    return 0;
}


dbus_bool_t add_watch(DBusWatch *w, void *data) {
    PollFD pfd;
    short pflags = 0;
    printf("calling %s watch=%p, enabled:%d\n", __func__, w, dbus_watch_get_enabled(w));
    if (!dbus_watch_get_enabled(w))
        return TRUE;
    DBusMonitor *d = (DBusMonitor*)data;
    dbmAddDBW(d, w);
    pfd.fd = dbus_watch_get_unix_fd(w);
    unsigned int flags = dbus_watch_get_flags(w);
    if(flags & DBUS_WATCH_READABLE) {
        pflags |= POLLIN;
    }
    if(flags & DBUS_WATCH_WRITABLE) {
        pflags |= POLLOUT;
    }
    pfd.events = pflags;
    pfds[pfdc++] = pfd;
    printf("added dbus watch fd=%d watch=%p, flags:%d\n", pfd.fd, w, flags);
    return TRUE;
    
}

void remove_watch(DBusWatch *w, void *data) {
    int i;
    printf("calling %s watch=%p, enabled:%d\n", __func__, w, dbus_watch_get_enabled(w));
    DBusMonitor *d = (DBusMonitor*)data;
    if(dbmRemoveDBW(d, w)) { //避免重复remove
        int fd = dbus_watch_get_unix_fd(w);
        for(i=0; i<pfdc; i++) {
            if(pfds[i].fd == fd) {
                pfds[i] = pfds[--pfdc];
                break; 
            }
        }
        printf("removed dbus watch watch=%p, fp:%d\n", w, fd);
    }
}


dbus_bool_t procCtlFD(DBusMonitor *m, PollFD *pfd) {
    DBMCtrl cmd = -1;
    if(pfd->revents & POLLIN) {
        read(pfd->fd, &cmd, sizeof(DBMCtrl));
        if(cmd == DBMSTOP) {
            return FALSE;
        }else {
            fprintf(stderr, "Unsupported DBusMonitor control cmd: %d!\n",
                    cmd);
            return TRUE;
        }
        
    }
    if(pfd->revents & POLLERR) {
        fprintf(stderr, "DBusMonitor control pipe broken!\n");
        return FALSE;
    }
    return TRUE;
}

void dispatch(DBusMonitor *m) {
    PollFD pfd;
    int nfds;
    int i;
    int cfd = dbmGetCtrlFd(m); //ctrl fd
    pfd.fd = cfd;
    pfd.events = POLLIN;
    pfds[pfdc++] = pfd;
    while(1){
        nfds = poll(pfds, pfdc, -1);
        if(nfds<0) {
            perror("DBusMonitor mainloop execption");                               
            break;      
        }
        for(i=0; i<pfdc; i++) {
            if(pfds[i].fd == cfd && !procCtlFD(m, &pfds[i])) {
                goto OUT;
            }
            if(pfds[i].revents & POLLIN) {
                dbmHandleDBusRead(m, pfds[i].fd);
            }
            if(pfds[i].revents & POLLOUT) {
                dbmHandleDBusWrite(m, pfds[i].fd);
            }
            if(pfds[i].revents & POLLERR) {
                dbmHandleDBusExecption(m, pfds[i].fd);
            }
        }
    }
    OUT:
    dbmCloseConnection(m);
}
